//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Windows;
    using ManagementStudioServices.Interfaces;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using SR = Microsoft.Research.DataLayer;

    /// <summary>
    /// The presenter for the center pane of node.
    /// </summary>
    public class NodeCenterPanePresenter : IRegistryManagerCenterPanePresenter, INotifyPropertyChanged
    {
        #region Private fields

        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The registry manager instance. Helper class for the registry calls.
        /// </summary>
        private IRegistryManagerService registryManagerService;

        /// <summary>
        /// The description of the node currently selected.
        /// </summary>
        private string nodeDescription;

        /// <summary>
        /// The selected node.
        /// </summary>
        private NodeViewModel selectedNode;

        /// <summary>
        /// The machine availability.
        /// </summary>
        private SR.ExecutorAvailabiltiy machineAvailability;

        /// <summary>
        /// A flag to check if a property has been modified.
        /// </summary>
        private bool propertyModified;

        /// <summary>
        /// property to hold the concurrent jobs numbers
        /// </summary>
        private int maxConcurrentJobs;

        private string clusterUser;
        private string clusterPassword;
        private string installedDirectory;

        #endregion

        #region Properties

        /// <summary>
        /// The currently selected node.
        /// </summary>
        public NodeViewModel SelectedNode
        {
            get
            {
                return this.selectedNode;
            }
            set
            {
                this.selectedNode = value;
                if (this.selectedNode != null)
                {
                    this.NodeDescription = this.selectedNode.Description;
                    this.MachineAvailability = this.selectedNode.ExecutorAvailability;
                    this.MachineStatus = this.selectedNode.MachineStatus;
                    this.MaxConcurrentJobs = this.selectedNode.MaxConcurrentJobs;
                    
                    this.ClusterUser = this.selectedNode.ClusterUser;
                    this.ClusterPassword= this.selectedNode.ClusterPassword;
                    this.ComputeInstalledDirectory = this.selectedNode.InstalledDir;
                    this.propertyModified = false;
                }

                this.OnNotifyPropertyChanged("SelectedNode");
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// The description of the node currently selected.
        /// </summary>
        public string NodeDescription
        {
            get
            {
                return this.nodeDescription;
            }
            set
            {
                this.nodeDescription = value;
                this.OnNotifyPropertyChanged("NodeDescription");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// Gets/sets the max concurrent jobs on a node
        /// </summary>
        public int MaxConcurrentJobs
        {
            get
            {
                return this.maxConcurrentJobs;
            }
            set
            {
                maxConcurrentJobs = value;
                this.OnNotifyPropertyChanged("MaxConcurrentJobs");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }



        /// <summary>
        /// Gets the Installed Directory
        /// </summary>
        public string ComputeInstalledDirectory
        {
            get
            {
                return this.installedDirectory;
            }
            set
            {
                installedDirectory = value;
                this.OnNotifyPropertyChanged("ComputeInstalledDirectory");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// Gets the Cluster username 
        /// </summary>
        public string ClusterUser
        {
            get
            {
                return this.clusterUser;
            }
            set
            {
                this.clusterUser = value;
                this.OnNotifyPropertyChanged("ClusterUser");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// Gets the Cluster password
        /// </summary>
        public string ClusterPassword
        {
            get
            {
                return this.clusterPassword;
            }
            set
            {
                NodeEditorContentView conview = (NodeEditorContentView)this.ContentView;
                if ((null != conview) && conview.Pushback)
                {
                    if (conview.ClusterAdminPassword != this.clusterPassword)
                    {
                        conview.ClusterAdminPassword = value;
                    }
                }
                this.clusterPassword = value;
                this.OnNotifyPropertyChanged("ClusterPassword");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// The availability of the node selected.
        /// </summary>
        public SR.ExecutorAvailabiltiy MachineAvailability
        {
            get
            {
                return this.machineAvailability;
            }
            set
            {
                this.machineAvailability = value;
                this.OnNotifyPropertyChanged("MachineAvailability");
                this.propertyModified = true;
                this.RaiseCanExecuteEvents();
            }
        }

        /// <summary>
        /// The status of the node selected.
        /// </summary>
        public SR.MachineStatus MachineStatus { get; set; }

        #endregion Properties

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">The unity container instance. </param>
        /// <param name="contentView">The center pane view.</param>
        /// <param name="leftPane">The leftpane view for the node editor.Contains the list of nodes.</param>
        /// <param name="service">The registry manager service.</param>
        public NodeCenterPanePresenter(IUnityContainer container, NodeEditorContentView contentView, IRegistryManagerService service)
        {
            this.container = container;
            this.ContentView = contentView;
            this.registryManagerService = service;
            contentView.DataContext = this;

            // Initialize commands
            this.NodeSaveCommand = new DelegateCommand<object>(p => this.NodeSaveCommandExecute(), p => this.MachineModifiedCanExecute());
            this.NodeCancelCommand = new DelegateCommand<object>(p => this.NodeCancelCommandExecute(), p => this.MachineModifiedCanExecute());
            this.NodeDeleteCommand = new DelegateCommand<object>(p => this.NodeDeleteCommandExecute());
        }

        #endregion Constructor

        #region Commands

        /// <summary>
        /// The command for the node saved operation.
        /// </summary>
        public DelegateCommand<object> NodeSaveCommand { get; private set; }

        /// <summary>
        /// The command for the node edit cancel operation.
        /// </summary>
        public DelegateCommand<object> NodeCancelCommand { get; private set; }

        /// <summary>
        /// The command for the node delete operation.
        /// </summary>
        public DelegateCommand<object> NodeDeleteCommand { get; private set; }
        
        #endregion

        #region IRegistryManagerCenterPanePresenter Members

        /// <summary>
        /// Gets or sets the content view.
        /// </summary>
        /// <value>The content view.</value>
        public UIElement ContentView
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the refresh command.
        /// </summary>
        /// <value>The refresh command.</value>
        public DelegateCommand<BaseRegMgrModel> RefreshCommand
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the selected model.
        /// </summary>
        /// <value>The selected model.</value>
        public BaseRegMgrModel SelectedModel
        {
            get
            {
                return this.SelectedNode;
            }
            set
            {
                if ((null != value) && (this.SelectedNode != null) && (!value.Id.Equals(this.SelectedNode.Id)))
                {
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.SelectedNode = value as NodeViewModel;
            }
        }

        /// <summary>
        /// Saves the changes.
        /// </summary>
        /// <returns>
        /// An enum value indicating Yes, No or Cancel.
        /// </returns>
        public SaveStateHandler SaveChanges()
        {
            return this.EnforceSaveForChanges();
        }

        /// <summary>
        /// Gets or sets the delete command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> DeleteCommand
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the update command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> UpdateCommand { get; set; }
        
        /// <summary>
        /// Gets or sets the select root command.
        /// </summary>
        /// <value>The select root command.</value>
        public DelegateCommand<object> SelectRootCommand { get; set; }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called when [notify property changed].
        /// </summary>
        /// <param name="name">The name.</param>
        protected void OnNotifyPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        #endregion

        #region Methods

        /// <summary>
        /// Save the node.
        /// </summary>
        private void NodeSaveCommandExecute()
        {
            try
            {
                if (this.selectedNode != null)
                {
                    this.selectedNode.Description = this.nodeDescription;
                    this.selectedNode.ExecutorAvailability = this.MachineAvailability;
                    this.selectedNode.MaxConcurrentJobs = this.MaxConcurrentJobs;
                    this.selectedNode.ClusterPassword = this.ClusterPassword;
                    this.selectedNode.ClusterUser = this.ClusterUser;
                    this.SelectedNode.InstalledDir = this.ComputeInstalledDirectory;
                    SR.Machine editedNode = this.registryManagerService.SaveNode(selectedNode);
                    if (editedNode != null)
                    {
                        selectedNode.UpdateModel(editedNode);
                    }

                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusNodeSaved"));
                    this.propertyModified = false;
                    this.RaiseCanExecuteEvents();
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// Cancel the edits made to the node.
        /// </summary>
        private void NodeCancelCommandExecute()
        {
            this.Rollback();
            if (this.SelectRootCommand != null)
            {
                this.SelectRootCommand.Execute(null);
            }
        }

        /// <summary>
        /// Delete the node.
        /// </summary>
        private void NodeDeleteCommandExecute()
        {
            try
            {
                if (selectedNode != null)
                {
                    // If jobschedules exist then warn the user.
                    MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                           ManagementStudioResourceManager.GetString("DeleteNodeConfirmation"));

                    // If Yes then delete the node as well as pending schedules.
                    if (result == MessageBoxResult.Yes)
                    {
                        this.registryManagerService.DeleteNode(this.selectedNode.Id);
                        this.selectedNode.IsDeleted = true;
                        if (this.DeleteCommand != null)
                        {
                            this.DeleteCommand.Execute(this.selectedNode as BaseRegMgrModel);
                        }
                    }

                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusNodeDeleted"));
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// Checks if the save and delete button can be enabled.
        /// </summary>
        /// <returns>True if the save/cancel button is to be enabled.</returns>
        private bool MachineModifiedCanExecute()
        {
            return this.propertyModified;
        }

        /// <summary>
        /// Save the changes to the registry.
        /// </summary>
        private SaveStateHandler EnforceSaveForChanges()
        {
            if (this.selectedNode != null && !this.selectedNode.IsDeleted && this.propertyModified)
            {
                MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("SaveChangesToNode"));
                if (result == MessageBoxResult.Yes)
                {
                    this.NodeSaveCommand.Execute(this.selectedNode);
                }
                else
                {
                    this.Rollback();
                }
            }
            
            return new SaveStateHandler(SaveStatus.Valid);
        }

        /// <summary>
        /// Raise the can execute events so that the UI elements can be enabled/disabled.
        /// </summary>
        private void RaiseCanExecuteEvents()
        {
            this.NodeSaveCommand.RaiseCanExecuteChanged();
            this.NodeCancelCommand.RaiseCanExecuteChanged();
            this.NodeDeleteCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Rollbacks this instance.
        /// </summary>
        private void Rollback()
        {
            if (this.selectedNode != null)
            {
                this.NodeDescription = this.selectedNode.Description;
                this.MachineAvailability = this.selectedNode.ExecutorAvailability;
                this.propertyModified = false;
            }

            this.RaiseCanExecuteEvents();
        }

        #endregion Methods
    }
}
